use crate::AppState;
use crate::entities::users;
use anyhow::{Context, bail};
use axum::{Router, extract::Path, routing::get};
use axum_ifnk_common::{
    app_error,
    prelude::{AppError, ContextExt, Response, ResultExt},
};
use chrono::Utc;

/// 测试路由定义
pub fn routes() -> Router<AppState> {
    Router::<AppState>::new()
        .route("/test/success", get(test_success))
        .route("/test/simple-error", get(test_simple_error))
        .route("/test/error-with-detail", get(test_error_with_detail))
        .route("/test/panic", get(test_panic))
        .route("/test/question-mark", get(test_question_mark))
        .route("/test/anyhow-error", get(test_anyhow_error))
        .route("/test/parse-error/{id}", get(test_parse_error))
        .route("/test/validation/{age}", get(test_validation))
        .route("/test/context", get(test_context))
        .route("/test/with-code", get(test_with_code))
        .route("/test/macro", get(test_macro))
}

/// 测试 1: 成功响应
#[utoipa::path(
    get,
    path = "/test/success",
    responses(
        (status = 200, description = "成功响应示例")
    ),
    tag = "错误处理测试"
)]
async fn test_success() -> Response<users::Model> {
    Response::success(sample_user(1, "测试用户"))
}

/// 测试 2: 简单错误(不带 detail)
#[utoipa::path(
    get,
    path = "/test/simple-error",
    responses(
        (status = 200, description = "简单错误响应")
    ),
    tag = "错误处理测试"
)]
async fn test_simple_error() -> Response<users::Model> {
    Response::failed("这是一个简单的错误消息")
}

/// 测试 3: 带详细信息的错误
#[utoipa::path(
    get,
    path = "/test/error-with-detail",
    responses(
        (status = 200, description = "带详细信息的错误响应")
    ),
    tag = "错误处理测试"
)]
async fn test_error_with_detail() -> Response<users::Model> {
    Response::failed_with_detail(
        "用户名长度验证失败",
        "错误详情: 用户名长度为 2,小于最小长度 3\n\n上下文:\n  username: ab\n  min_length: 3\n  current_length: 2",
    )
}

/// 测试 4: Panic 错误(会被中间件捕获)
#[utoipa::path(
    get,
    path = "/test/panic",
    responses(
        (status = 200, description = "测试 panic 捕获")
    ),
    tag = "错误处理测试"
)]
async fn test_panic() -> Response<String> {
    panic!("这是一个故意触发的 panic,用于测试中间件捕获!");
}

/// 测试 5: 使用 ? 操作符的错误传播
#[utoipa::path(
    get,
    path = "/test/question-mark",
    responses(
        (status = 200, description = "测试 ? 操作符错误传播")
    ),
    tag = "错误处理测试"
)]
async fn test_question_mark() -> Result<Response<String>, AppError> {
    // 调用一个返回 Result 的函数,使用 ? 传播错误
    let result = risky_operation()?;
    Ok(Response::success(result))
}

/// 测试 6: anyhow 错误链
#[utoipa::path(
    get,
    path = "/test/anyhow-error",
    responses(
        (status = 200, description = "测试 anyhow 错误链")
    ),
    tag = "错误处理测试"
)]
async fn test_anyhow_error() -> Result<Response<String>, AppError> {
    // 模拟多层错误链
    let result = level_1_operation().context("顶层操作失败")?;

    Ok(Response::success(result))
}

/// 测试 7: 解析错误(? 操作符自动转换)
#[utoipa::path(
    get,
    path = "/test/parse-error/{id}",
    params(
        ("id" = String, Path, description = "用户ID(输入非数字测试错误)")
    ),
    responses(
        (status = 200, description = "测试解析错误")
    ),
    tag = "错误处理测试"
)]
async fn test_parse_error(Path(id): Path<String>) -> Result<Response<u64>, AppError> {
    // 这里如果 id 不是有效数字,parse 会失败
    // ? 操作符会自动将 ParseIntError 转换为 AppError
    let user_id = id.parse::<u64>()?;

    Ok(Response::success(user_id))
}

/// 测试 8: 验证错误(使用 app_error! 宏)
#[utoipa::path(
    get,
    path = "/test/validation/{age}",
    params(
        ("age" = i32, Path, description = "年龄(< 18 会报错)")
    ),
    responses(
        (status = 200, description = "测试验证错误")
    ),
    tag = "错误处理测试"
)]
async fn test_validation(Path(age): Path<i32>) -> Result<Response<String>, AppError> {
    if age < 18 {
        return Err(app_error!(400, "年龄必须 >= 18,当前年龄: {}", age));
    }

    if age > 120 {
        return Err(app_error!(400, "年龄不合理,当前年龄: {}", age));
    }

    Ok(Response::success(format!("年龄验证通过: {}", age)))
}

/// 测试 9: 使用 ResultExt::context 添加上下文
#[utoipa::path(
    get,
    path = "/test/context",
    responses(
        (status = 200, description = "测试 context 扩展")
    ),
    tag = "错误处理测试"
)]
async fn test_context() -> Result<Response<String>, AppError> {
    // 使用 ResultExt 的 context 方法添加上下文
    // 注意:需要导入 ResultExt trait
    let result = ResultExt::context(risky_io_operation(), "读取配置文件失败")?;

    Ok(Response::success(result))
}

/// 测试 10: 使用 ResultExt::with_code 指定错误码
#[utoipa::path(
    get,
    path = "/test/with-code",
    responses(
        (status = 200, description = "测试 with_code 扩展")
    ),
    tag = "错误处理测试"
)]
async fn test_with_code() -> Result<Response<String>, AppError> {
    // 使用 with_code 指定错误码为 404
    let result = risky_io_operation().with_code(404)?;

    Ok(Response::success(result))
}

/// 测试 11: 使用 ContextExt::c 简化 context 调用
#[utoipa::path(
    get,
    path = "/test/macro",
    responses(
        (status = 200, description = "测试简化的 c() 方法")
    ),
    tag = "错误处理测试"
)]
async fn test_macro() -> Result<Response<String>, AppError> {
    // 使用 ContextExt 的 c() 方法(更简洁)
    let result = risky_io_operation().c("读取配置失败")?;

    Ok(Response::success(result))
}

// ========== 辅助函数 ==========

/// 构造一个示例用户, 用于演示和测试返回值
fn sample_user(id: i32, username: &str) -> users::Model {
    let now = Utc::now();
    users::Model {
        id,
        username: username.to_string(),
        email: None,
        created_at: now,
        updated_at: now,
    }
}

/// 模拟一个可能失败的操作
fn risky_operation() -> Result<String, AppError> {
    Err(AppError::new(
        "数据库连接超时: connection refused at 127.0.0.1:3306",
    ))
}

/// 模拟 IO 操作
fn risky_io_operation() -> std::io::Result<String> {
    Err(std::io::Error::new(
        std::io::ErrorKind::NotFound,
        "配置文件不存在: config.toml",
    ))
}

/// 模拟三层错误链 - 第一层
fn level_1_operation() -> anyhow::Result<String> {
    level_2_operation().context("第一层:处理用户请求失败")?;
    Ok("success".to_string())
}

/// 模拟三层错误链 - 第二层
fn level_2_operation() -> anyhow::Result<String> {
    level_3_operation().context("第二层:查询数据库失败")?;
    Ok("success".to_string())
}

/// 模拟三层错误链 - 第三层(最底层错误)
fn level_3_operation() -> anyhow::Result<String> {
    bail!("第三层:连接超时 - Connection timed out after 5s");
}
